const http = require('http');
const connect = require('connect');
const serveStaticMiddleware = require('./middlewares/static.js');
const resolveConfig = require('../config.js');
const { createOptimizeDepsRun } = require('../optimizer/index.js');
const transformMiddleware = require('./middlewares/transform.js');
const { createPluginContainer } = require('./pluginContainer.js');
const { handleHMRUpdate } = require('./hmr');
const { createWebSocketServer } = require('./ws');
const { normalizePath } = require('../utils');
const chokidar = require('chokidar');
const { ModuleGraph } = require('./moduleGraph');
const path = require('path');

async function runOptimize(config, server) {
  const optimizeDeps = await createOptimizeDepsRun(config);
  // 此时把第三方模块和预编译后的文件关联保存在 metadata 中，也保存在了 _optimizeDepsMetadata 中
  server._optimizeDepsMetadata = optimizeDeps.metadata;
}

async function createServer() {
  const config = await resolveConfig();
  const middlewares = connect();

  const httpServer = http.createServer(middlewares);
  const ws = createWebSocketServer(httpServer, config);
  const watcher = chokidar.watch(path.resolve(config.root), {
    ignored: ['**/node_modules/**', '**/.git/**'],
  });
  const moduleGraph = new ModuleGraph((url) => pluginContainer.resolveId(url));

  const pluginContainer = await createPluginContainer(config);

  const server = {
    config,
    ws,
    watcher,
    moduleGraph,
    httpServer,
    pluginContainer,
    async listen(port) {
      // 在项目启动前进行依赖的与构建
      // 1. 找到本项目依赖的第三方模块
      await runOptimize(config, server);

      httpServer.listen(port, async () => {
        console.log(`server running at http://localhost:${port}`);
      });
    },
  };

  // 文件发生变化时会执行该回调函数
  watcher.on('change', async (file) => {
    file = normalizePath(file);
    await handleHMRUpdate(file, server);
  });

  for (const plugin of config.plugins) {
    if (plugin.configureServer) {
      await plugin.configureServer(server);
    }
  }
  middlewares.use(transformMiddleware(server));
  middlewares.use(serveStaticMiddleware(config));

  return server;
}

exports.createServer = createServer;
